﻿using Plexi.Tools;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace Plexi.Configuration
{
	/// <summary>Contains static methods that have to do with loading data from the configuration file.</summary>
	public static class ConfigLoader
	{
		private const string _configFilePath = "GameData/PLEXI/PLEXIConfiguration.txt";

		private static string LoadString(string path)
		{
			string all = null;
			try
			{
				using (StreamReader sr = new StreamReader(string.Format("{0}/{1}", KSPUtil.ApplicationRootPath, path)))
					all = sr.ReadToEnd();
			}
			catch (IOException ex)
			{
				Log.Add("ConfigLoader ERROR: failed to load {0}/{1}. See the exception below.", KSPUtil.ApplicationRootPath, path);
				Log.Add(ex.Message);
			}
			if (string.IsNullOrEmpty(all))
				Log.Add(string.Format("ConfigLoader: {0} was empty.", path));
			return all ?? "";
		}

		public static void LoadConfigIfNecessary()
		{
			if (Config.IsInitialized)
				return;
#if(DEBUG)
			Log.Add("Loading config...");
#endif
			string all = LoadString(_configFilePath);
			if (string.IsNullOrEmpty(all))
				return;
			string[] lines = all.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
			string line, type;
			int j;
			for (int i = 0; i < lines.Length; i++)
			{
				line = lines[i].Trim();
				if (line.StartsWith("//") || line.Length == 0)
					continue;
				j = 0;
				type = string.Empty;
				while (!line[j].Equals(' ') && j <= line.Length - 1)
				{
					type += line[j];
					j++;
				}
				if (j == line.Length - 1 || type.Length == 0)
				{
					Log.Add("ERROR: configloader: the following line is malformed: {0}", line);
					continue;
				}
				switch (type)
				{
					case "PolarCircles": { ParsePolarCircle(line.Substring(j).Trim()); break; }
					case "ForbiddenBodies": { ParseForbiddenBodies(line.Substring(j).Trim()); break; }
					case "ForbiddenArea": { ParseForbiddenArea(line.Substring(j).Trim()); break; }
					default: { Log.Add("ERROR: configloader: the following line is malformed: {0}", line); break; }
				}
			}
			Config.IsInitialized = true;
#if(DEBUG)
			Log.Add("Config successfully loaded.");
#endif
		}

		private static void ParseForbiddenArea(string line)
		{
			char[] lineSplitter = new char[] { ',' }, nodeSplitter = new char[] { ' ' }, bodySplitter = new char[] { ':' };
			string node;
			float latMin = 0, latMax = 0, lonMin = 0, lonMax = 0;
			float nodeValue;
			string[] nodes, nodeParts, lineParts;
			bool lineFail = false;
			CelestialBody body;
			lineParts = line.Split(bodySplitter, StringSplitOptions.RemoveEmptyEntries);
			if (lineParts.Length != 2)
			{
				Log.Add("ERROR: ParseForbiddenArea: line has the wrong number of parts! Needs to have exactly one colon per line. Line: {0}", line);
				return;
			}
			body = FlightGlobals.Bodies.FindByName(lineParts[0].Trim());
			if (body == null)
			{
				Log.Add("ERROR: ParseForbiddenArea: couldn't find celestial body with name {0}. Line: {1}", lineParts[0].Trim(), line);
				return;
			}
			nodes = lineParts[1].Trim().Split(lineSplitter, StringSplitOptions.RemoveEmptyEntries);
			if (nodes.Length != 4)
			{
				Log.Add("ERROR: ParseForbiddenArea: line has the wrong number of nodes! Needs one of each of the following: LatMin, LatMax, LonMin, LonMax. Line: {0}", line);
				return;
			}
			for (int j = 0; j < nodes.Length; j++)
			{
				node = nodes[j];
				nodeParts = node.Split(nodeSplitter, StringSplitOptions.RemoveEmptyEntries);
				if (nodeParts.Length != 2)
				{
					Log.Add("ERROR: ParseForbiddenArea: node {0} is badly formatted! Needs to be a name and a value separated by a space. Line: {1}", j, line);
					lineFail = true;
					break;
				}
				if (!float.TryParse(nodeParts[1].Trim(), out nodeValue)) //only because all node values are numbers
				{
					Log.Add("ERROR: ParseForbiddenArea: node {0}: couldn't parse value: {1}. Line: {2}", j, nodeParts[1].Trim(), line);
					lineFail = true;
					break;
				}
				switch (nodeParts[0].Trim().ToLower())
				{
					case "latmin": { latMin = nodeValue; break; }
					case "latmax": { latMax = nodeValue; break; }
					case "lonmin": { lonMin = nodeValue; break; }
					case "lonmax": { lonMax = nodeValue; break; }
					default:
						{
							Log.Add("ERROR: ParseForbiddenArea: node {0}: invalid node name {1}, should be one of the following: LatMin, LatMax, LonMin, LonMax. Line: {2}", j, nodeParts[0].Trim(), line);
							lineFail = true;
							break;
						}
				}
				if (lineFail)
					break;
			}
			if (lineFail)
				return;
			if (Config.ForbiddenAreas == null)
				Config.ForbiddenAreas = new List<Area>();
			Config.ForbiddenAreas.Add(new Area()
			{
				BodyIndex = body.flightGlobalsIndex,
				LatMax = latMax,
				LatMin = latMin,
				LonMax = lonMax,
				LonMin = lonMin
			});
#if(DEBUG)
			Log.Add("ParseForbiddenArea: loaded area: {0}: LatMax: {1}, LatMin: {2}, LonMax: {3}, LonMin: {4}", body.bodyName, latMax, latMin, lonMax, lonMin);
#endif
		}

		private static void ParseForbiddenBodies(string line)
		{
			string[] bodies = line.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			CelestialBody body;
			Config.ForbiddenBodyIndices = new List<int>();
			foreach (string bodyName in bodies)
			{
				body = FlightGlobals.Bodies.FindByName(bodyName.Trim());
				if (body != null)
				{
					Config.ForbiddenBodyIndices.Add(body.flightGlobalsIndex);
#if(DEBUG)
					Log.Add("ParseForbiddenBodies: {0} is a forbidden body.", body.bodyName);
#endif
				}
#if(DEBUG)
				else
					Log.Add("ParseForbiddenBodies: forbidden body {0} not found in-game.", body.bodyName);
#endif
			}
		}

		private static void ParsePolarCircle(string line)
		{
			char[] dataSplitter = new char[] { ';' }, bodySplitter = new char[] { ':' };
			string[] dataParts, lineParts;
			float arctic, antarctic;
			CelestialBody body;
			lineParts = line.Split(bodySplitter, StringSplitOptions.RemoveEmptyEntries);
			if (lineParts.Length != 2)
			{
				Log.Add("ERROR: ParsePolarCircle: line has the wrong number of parts! Needs to have exactly one colon per line. Line: {0}", line);
				return;
			}
			body = FlightGlobals.Bodies.FindByName(lineParts[0].Trim());
			if (body == null)
			{
				Log.Add("ERROR: ParsePolarCircle: couldn't find celestial body with name {0}. Line: {1}", lineParts[0].Trim(), line);
				return;
			}
			dataParts = lineParts[1].Trim().Split(dataSplitter, StringSplitOptions.RemoveEmptyEntries);
			if (dataParts.Length != 2)
			{
				Log.Add("ERROR: ParsePolarCircle: line has the wrong number of semicolons (needs to have only one). Faulty line: {0}", line);
				return;
			}
			if (!float.TryParse(dataParts[0].Trim(), out antarctic) ||
				!float.TryParse(dataParts[1].Trim(), out arctic))
			{
				Log.Add("ERROR: ParsePolarCircle: couldn't parse one of the values in the following line: {0}", line);
				return;
			}
			if (Config.PolarCircles == null)
				Config.PolarCircles = new PolarCircles();
			Config.PolarCircles.Add(body.flightGlobalsIndex, new PolarInfo() { AntarcticLatitude = antarctic, ArcticLatitude = arctic });
#if(DEBUG)
			Log.Add("ParsePolarCircle: loaded polar circles for {0}: arctic: {1}, antarctic: {2}.", body.bodyName, arctic, antarctic);
#endif
		}

	}
}
